﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace Pyramid.IO
{
    public class MemoryStream
        : Stream
    {
        private const int defaultCapacity = 0;

        private List<Tryte> tryteStore;

        private long position;

        public MemoryStream()
            : this(defaultCapacity)
        {
        }

        public MemoryStream(int capacity)
        {
            this.tryteStore = new List<Tryte>(capacity);
        }

        public override bool CanRead
        {
            get
            {
                return true;
            }
        }

        public override bool CanSeek
        {
            get
            {
                return true;
            }
        }

        public override bool CanWrite
        {
            get
            {
                return true;
            }
        }

        public override void Flush()
        {
            // Do nothing
        }

        public override long Length
        {
            get
            {
                return this.tryteStore.Count;
            }
        }

        public override long Position
        {
            get
            {
                return this.position;
            }
            set
            {
                if (value > int.MaxValue)
                {
                    throw new NotSupportedException("Setun.IO.MemoryStream does not support streams greater than System.Int32.MaxValue in length");
                }

                if (value > this.tryteStore.Count)
                {
                    this.tryteStore.AddRange(Enumerable.Repeat((Tryte)0, (int)(value - this.tryteStore.Count)));
                }

                this.position = value;
            }
        }

        public override int Read(Tryte[] buffer, int offset, int count)
        {
            int numberOfValuesToReturn = (int)(this.Length - this.Position);

            if (numberOfValuesToReturn > count)
            {
                numberOfValuesToReturn = count;
            }

            if (numberOfValuesToReturn <= 0)
            {
                return 0;
            }

            for (int valueIndex = 0; valueIndex < numberOfValuesToReturn; valueIndex++)
            {
                buffer[offset + valueIndex] = this.tryteStore[valueIndex + (int)this.Position];
            }

            this.Position += numberOfValuesToReturn;

            return numberOfValuesToReturn;
        }

        public override long Seek(long offset, System.IO.SeekOrigin origin)
        {
            switch (origin)
            {
                case System.IO.SeekOrigin.Begin:
                    this.Position = offset;
                    break;
                case System.IO.SeekOrigin.Current:
                    this.Position += offset;
                    break;
                case System.IO.SeekOrigin.End:
                    this.Position = this.Length - offset;
                    break;
                default:
                    throw new ArgumentException("Unexpected SeekOrigin.", "origin");
            }

            return this.Position;
        }

        public override void SetLength(long value)
        {
            throw new NotImplementedException();
        }

        public override void Write(Tryte[] buffer, int offset, int count)
        {
            for (int valueIndex = 0; valueIndex < count; valueIndex++)
            {
                if (valueIndex + (int)this.Position < this.Length)
                {
                    this.tryteStore[valueIndex + (int)this.Position] = buffer[offset + valueIndex];
                }
                else
                {
                    this.tryteStore.Add(buffer[offset + valueIndex]);
                }
            }

            this.Position += count;
        }
    }
}
